﻿using Shake.Framework.Attributes;
using Shake.Framework.Context;
using Shake.Framework.Enums;
using Shake.Framework.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;

namespace Shake.Framework.Services
{
    public class SubscribeService : ISubscribeService
    {
        private static readonly List<SubscriberContext> _subscriberContexts;

        static SubscribeService()
        {
            _subscriberContexts = new List<SubscriberContext>();
            var features = FeatureUtil.GetFeatureAssembly();
            foreach (var feature in features)
            {
                var classes = feature.ExportedTypes;
                // 获取带有订阅的方法的类
                var withSubscriberCls = classes.Where(t => t.GetMethods().Any(s => s.GetCustomAttribute(typeof(SubscribeAttribute)) != null));
                foreach (var cls in withSubscriberCls)
                {
                    // 获取类中有订阅的方法
                    var methods = cls.GetMethods().Where(t => t.GetCustomAttributes(typeof(SubscribeAttribute)).Any());
                    var subcriberContexts = GetSubcriberContextsFrom(methods);
                    _subscriberContexts.AddRange(subcriberContexts);
                }
            }
        }

        private static IEnumerable<SubscriberContext> GetSubcriberContextsFrom(IEnumerable<MethodInfo> methodInfos)
        {
            foreach (var method in methodInfos)
            {
                var SubscribeAttributes = method.GetCustomAttributes(typeof(SubscribeAttribute)).Cast<SubscribeAttribute>();
                foreach (var SubscribeAttribute in SubscribeAttributes)
                {
                    yield return new SubscriberContext
                     {
                         Subscriber = method,
                         Subject = SubscribeAttribute.Subject
                     };
                }
            }
        }

        public void Trigger(Subject subject, params object[] args)
        {
            var subscribersContexts = _subscriberContexts.Where(t => t.Subject == subject).ToList();
            if (subscribersContexts.Count == 0) return;

            foreach (var subscribersContext in subscribersContexts)
            {
                var parameterInfos = subscribersContext.Subscriber.GetParameters();

                // 以第一个接口为对象进行获取
                var @interface = subscribersContext.Subscriber.DeclaringType.GetInterfaces().FirstOrDefault();
                if (@interface == null) continue;

                var services = DependencyResolver.Current.GetServices(@interface).ToList();
                var instance = services[0];

                if (parameterInfos.Length == 0)
                {
                    subscribersContext.Subscriber.Invoke(instance, null); // 无参的方法，直接调用
                }
                else
                {
                    if (args == null || args.Length < parameterInfos.Length) throw new ArgumentException("订阅方法的参数长度和触发事件的长度不一致");
                    var parameters = new object[parameterInfos.Length];

                    for (var i = 0; i < parameterInfos.Length; i++)
                    {
                        parameters[i] = ConvertEx.ChangeType(args[i], parameterInfos[i].ParameterType);
                    }
                    subscribersContext.Subscriber.Invoke(instance, parameters);
                }
            }
        }
    }
}
